/*
 ============================================================================
 Name        : headerProcess.c
 Author      :
 Version     :	1
 Copyright   : Your copyright notice
 Description : Processing the content of each of the sections of the file to be disassembled
 ============================================================================
 */
#include "headerProcess.h"
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include "process/decode/decode.h"
#include <myLib.h>

#define HEADER_NAME_TEXT ".text"

char processText(GElf_Shdr shdr, Elf* e, size_t shstrndx, int fd); ///<Function that processes the text contained within a section of the file
/*! \struct s_headerProcess;
 \brief structure containing the name and function of each section to disassemble
 */
typedef struct
///Structure containing the name and function of each section to disassemble
{
	char *name; ///<Section Name
	char (*func)(GElf_Shdr shdr, Elf* e, size_t shstrndx, int fd); ///<Function to be process.
} s_headerProcess;

s_headerProcess headerFunctions[] = ///<Array of header functions
{
{ HEADER_NAME_TEXT, processText },
{ 0, 0 } };

/*! \fn processHeader(GElf_Shdr shdr, Elf* e, size_t shstrndx, int fd)
 \brief
 \param shdr ELF object containing the information for each section of the file
 \param e Elf object containing the information of the input file
 \param shstrndx Size of ELF section
 \param fd Pointer to the input file
 */
char processHeader(GElf_Shdr shdr, Elf* e, size_t shstrndx, int fd)
{
	char *name;

	s_headerProcess *pFunc = headerFunctions;
	while (pFunc->name != 0)
	{

		if ((name = elf_strptr(e, shstrndx, shdr.sh_name)) == NULL )
		{
			PRINT_ERROR("elf_strptr () failed : % s . ", elf_errmsg(-1));
			return STATUS_ERROR;
		}
		PRINT_DEBUG(DEBUG_ALL, "Section %s\n", name);
		//PRINT_DEBUG("   size %x\n", shdr.sh_size);
		//PRINT_DEBUG("   offs %x\n", shdr.sh_offset);
		if (strcmp(name, headerFunctions->name) == 0)
		{
			headerFunctions->func(shdr, e, shstrndx, fd);
		}
		pFunc++;
	}
	return STATUS_SUCCESS;
}

#define RAW_PRINT_LENGTH 16 ///<Raw print length
/*! \fn readBinHeaderByte(GElf_Shdr shdr, int fd, int offset)
 \brief Reads the binary contents of a byte of section established.
 \param shdr ELF object containing the information of a specific section of the input file
 \param fd Pointer to the input file
 \param offset Establishes position where you start reading
 */
unsigned char readBinHeaderByte(GElf_Shdr shdr, int fd, int offset)
{
	unsigned char buf;
	lseek(fd, shdr.sh_offset + offset, SEEK_SET);
	read(fd, &buf, 1);
	return buf;
}
/*! \fn readBinHeaderByte(GElf_Shdr shdr, int fd, int offset)
 \brief Reads the binary contents of a byte of section established.
 \param shdr ELF object containing the information of a specific section of the input file
 \param fd Pointer to the input file
 \param offset Establishes position where you start reading
 \param buf buffer containing the portion of the file that has been read
 \param size Portion size to be read
 */
char readBinHeaderData(GElf_Shdr shdr, int fd, int offset, char* buf, char size)
{
	lseek(fd, shdr.sh_offset + offset, SEEK_SET);
	read(fd, buf, size);
	return STATUS_SUCCESS;
}
/*! \fn rawPrintContent(int fd, GElf_Shdr shdr)
 \brief Sets the hex output format to the readBinHeaderByte function
 \param shdr ELF object containing the information of a specific section of the input file
 \param fd Pointer to the input file
 */
char rawPrintContent(int fd, GElf_Shdr shdr)
{
	char tCount = RAW_PRINT_LENGTH;
	unsigned int p = 0;
	while (p < shdr.sh_size)
	{
		tCount--;
		if (tCount)
		{
			printf("%02x ", readBinHeaderByte(shdr, fd, p));
		}
		else
		{
			printf("%02x \n", readBinHeaderByte(shdr, fd, p));
			tCount = RAW_PRINT_LENGTH;
		}
		p++;
	}
	printf("\n");
	return STATUS_SUCCESS;
}
/*! \fn printMachinePretty(MACHINE_INSTRUCTION * inst)
 \brief Sets output format to a machine instruction
 \param  inst Machine Instruction to be set
 */
void printMachinePretty(MACHINE_INSTRUCTION * inst)
{
	UINT8 * opPtr;
	UINT8 * isOpPtr;
	UINT8 * isOpValid;
	UINT8 counter = NUM_OPP_SUPPORTED;
	UINT8 opNumber = 0;


	printf("-------------------INSTRUCTION---------------\n");
	printf("Menemonic: %s\n", inst->mnemonic);
	printf("Opcode: %x\n", inst->opcode.value);


	if (inst->isPrefix) //Identifies if the instruction is a prefix
	{
		printf("Prefix: %x\n", (UINT8) inst->prefix.value);
	}
	if(inst->isModeReg) //Identifies if the instruction has a modeReg register
	{
		printf("ModReg: %x\n", (UINT8)inst->mod_reg_rm.value);
	}


	isOpPtr = inst->opSize;
	isOpValid = inst->opValid;
	while(counter)
	{
		opPtr = inst->op[opNumber];
		if(*isOpValid)
		{
			UINT8 opByte = 0;

			printf("Operand[%x]: ",opNumber);

			UINT8 opSize = BIT2BYTE(inst->opSize[counter]);

			while(opSize)
			{
				printf("%02x ",opPtr[opByte]);
				opSize--;
				opByte++;
			}
			printf("\n");
		}


		opNumber++;
		isOpValid++;
		opPtr++;
		isOpPtr++;
		counter--;
	}

	printf("--------------------------------------------\n");
}

/*! \fn printMachine(MACHINE_INSTRUCTION * inst)
 \brief Displays the hexadecimal number for the instruction processed
 \param inst Machine instruction to be set
 */
void printMachine(MACHINE_INSTRUCTION * inst)
{
	UINT8 * opPtr;
	UINT8 * isOpPtr;
	UINT8 * isOpValid;
	UINT8 counter = NUM_OPP_SUPPORTED;
	UINT8 opNumber = 0;

	if (inst->isPrefix)
	{
		printf("%02x ", (UINT8) inst->prefix.value);
	}
	printf("%02x ", inst->opcode.value);

	if (inst->isModeReg)
	{
		printf("%x ", (UINT8) inst->mod_reg_rm.value);
	}

	isOpPtr = inst->opSize;
	isOpValid = inst->opValid;
	while(counter)
	{
		opPtr = inst->op[opNumber];
		if(*isOpValid)
		{
			UINT8 opByte = 0;
			UINT8 opSize = BIT2BYTE(inst->opSize[counter]);

			while(opSize)
			{
				printf("%02x ",opPtr[opByte]);
				opSize--;
				opByte++;
			}

		}
		else
		{

		}


		opNumber++;
		isOpValid++;
		opPtr++;
		isOpPtr++;
		counter--;
	}
	printf("\n");
}
/*! \fn printAsm(MACHINE_INSTRUCTION * inst)
 \brief Displays the assembler instruction corresponding to the instruction analyzed
 \param inst Machine instruction to be set
 */
void printAsm(MACHINE_INSTRUCTION * inst)
{

	UINT8 opNumber = 0;


	//
	//Print ASM
	//
	printf("%s ", inst->mnemonic);


	opNumber = 0;

	while(opNumber < NUM_OPP_SUPPORTED)
	{
		if(inst->regOpValid[opNumber])
		{
			printf("%s ", inst->regOpp[opNumber]);
		}
		opNumber++;
	}

	opNumber = 0;
	while(opNumber < NUM_OPP_SUPPORTED)
	{
		if(inst->opValid[opNumber])
		{
			UINT8 opByte = 0;
			UINT8 opSize = BIT2BYTE(inst->opSize[opNumber]);
			printf("$");
			while (opSize)
			{
				printf("%02x", inst->op[opNumber][opByte]);
				opSize--;
				opByte++;
			}
		}
		opNumber++;
	}

	printf("\n");
}
/*! \fn processText(GElf_Shdr shdr, Elf* e, size_t shstrndx, int fd)
 \brief Function that processes the text contained within a section of the file
 \param shdr ELF object containing the information of a specific section of the input file
 \param e Elf object containing the information of the input file
 \param shstrndx Size of ELF section
 \param fd Pointer to the input file
*/
char processText(GElf_Shdr shdr, Elf* e, size_t shstrndx, int fd)
{
	char *name;
	UINT8 decodedSize = 0;
	//Elf_Scn *scn;
	//todo debug offset

	UINT32 offset = 0x00;
	char buf[MAX_INSTRUCTION_LENGTH];
	MACHINE_INSTRUCTION currentInstruction;
//	scn = NULL ;

		if ((name = elf_strptr(e, shstrndx, shdr.sh_name)) == NULL )
		{
			PRINT_ERROR("elf_strptr () failed : % s . ", elf_errmsg(-1));
			return STATUS_ERROR;
		}
		printf("Section: %s size: %x\n",name,(UINT8)shdr.sh_size);
		PRINT_DEBUG(DEBUG_ELF_HEADER_PROCESS, "Processing %s...\n", name);
		PRINT_DEBUG(DEBUG_ELF_HEADER_PROCESS, "   size %x\n", (UINT8)shdr.sh_size);
		PRINT_DEBUG(DEBUG_ELF_HEADER_PROCESS, "   offs %x\n", (UINT8)shdr.sh_offset);

		while (offset < shdr.sh_size)
		{
			zeromem(&currentInstruction, sizeof(MACHINE_INSTRUCTION));
			readBinHeaderData(shdr, fd, offset, buf, (INT8) MAX_INSTRUCTION_LENGTH);
			PRINT_DEBUG(DEBUG_DECODE_NEXT_INSTRUCTION,
						"$$$%08x:%02x\n",
						offset, (UINT8)*buf);
			decode(buf, &decodedSize, &currentInstruction);

			PRINT_DEBUG(DEBUG_ALL, "SIZE : %x\n", decodedSize);

			printAsm(&currentInstruction);
			printf("$%08x: ",offset);
			printMachine(&currentInstruction);

			printf("\n");
			//printMachine(&currentInstruction);

			offset += decodedSize;
		}

	return STATUS_SUCCESS;
}
